/*
   Copyright (C) 2009 Mohammad Nabil Al-Aggan
   (mohammad.nabil.h@gmail.com)

   This program is free software: you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation, either version 3 of the
   License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see
   <http://www.gnu.org/licenses/>.
*/

#include <itl/prayer.h>
#include <string.h> // for memset
#include <stdio.h> // for printf
#if HAVE_UNISTD_H
# include <unistd.h> // for getopt
#endif
#include <getopt.h>
#include <time.h>

// COLOR CODES
#define RESET		0
#define BRIGHT 		1
#define DIM		2
#define UNDERLINE 	3
#define BLINK		4
#define REVERSE		7
#define HIDDEN		8

#define BLACK 		0
#define RED		1
#define GREEN		2
#define YELLOW		3
#define BLUE		4
#define MAGENTA		5
#define CYAN		6
#define WHITE		7


//textcolor(BRIGHT, RED, BLACK);
//textcolor(RESET, WHITE, BLACK);
void textcolor(int attr, int fg, int bg)
{
  char command[13];

  /* Command is the control command to the terminal */
  if(attr == RESET)
    sprintf(command, "%c[%dm", 0x1B, attr);
  // test: ignoring color information when BRIGHT is passed
  // is not the intended behavior
  else if(attr == BRIGHT)
    sprintf(command, "%c[%dm", 0x1B, attr);
  else
    sprintf(command, "%c[%d;%d;%dm", 0x1B, attr, fg + 30, bg + 40);
  printf("%s", command);
}

void parse_args(int argc, const char *argv[])
{
#define ARG_DISABLED 0
#define ARG_REQUIRED 1
#define ARG_OPTIONAL 2

  // extern char *optarg;
  // extern int optind, opterr, optopt;

  static struct option long_options[] =
    {
      {"method",   ARG_REQUIRED, 0, 'm'},
      {"date",     ARG_REQUIRED, 0, 0},
      {"days",     ARG_REQUIRED, 0, 0},
      {"weeks",    ARG_REQUIRED, 0, 0},
      {"months",   ARG_REQUIRED, 0, 0},
      {"years",    ARG_REQUIRED, 0, 0},
      {"range",    ARG_REQUIRED, 0, 0},
      {"long",     ARG_REQUIRED, 0, 0},
      {"lat",      ARG_REQUIRED, 0, 0},
      {"gmt",      ARG_REQUIRED, 0, 0},

      {"dst",      ARG_DISABLED, 0, 0},
      {"imsaak",   ARG_DISABLED, 0, 'i'},
      {"qibla",    ARG_DISABLED, 0, 'q'},
      {"calendar", ARG_DISABLED, 0, 'c'},
      {"ramadan",  ARG_DISABLED, 0, 'r'},
      {"silent",   ARG_DISABLED, 0, 's'},
      {"tabular",  ARG_DISABLED, 0, 't'},
      {"version",  ARG_DISABLED, 0, 'v'},
      {"file",     ARG_DISABLED, 0, 'f'},
      {"hijri",    ARG_DISABLED, 0, 'j'},

      // --day (default behaviour), no need to put it
      {"week",     ARG_DISABLED, 0, 0},
      {"month",    ARG_DISABLED, 0, 0},
      {"quarter",  ARG_DISABLED, 0, 0},
      {"year",     ARG_DISABLED, 0, 0},

      {"location", ARG_REQUIRED, 0, 'l'},

      {0, 0, 0, 0}
    };
  char option_string[]= "m:d:iqcrstvfjl";

  int option_index;
  getopt_long(argc,argv,option_string,&long_options,&option_index);
}

void get_today(Date *date)
{
  time_t     now;
  struct tm  *ts;

  now = time(NULL);
  ts = localtime(&now);
  date->year  = ts->tm_year+1900; // 0 ==> 1900, fix that.
  date->month = ts->tm_mon+1; // month got is 0-based, fix that.
  date->day   = ts->tm_mday;
}

void get_time(int *hour, int *minute, int *second)
{
  time_t     now;
  struct tm  *ts;

  now = time(NULL);
  ts = localtime(&now);
  *hour   = ts->tm_hour;
  *minute = ts->tm_min;
  *second = ts->tm_sec;
}

Location location;
enum CITY {Cairo, Rennes};

//for modifiying the global variable location--> TODO: add more cities
void set_location(enum CITY c){
  //Auto timezone calculation  
  time_t rawtime;
  struct tm  ptm;
  time ( &rawtime );
  gmtime_r ( &rawtime,&ptm );
  struct tm *t = localtime(&rawtime);
  location.gmtDiff=t->tm_hour - ptm.tm_hour;

  location.dst=0; // Day light saving time set to 0 DO NOT CHANGE!
  

  /* Zero them all out */
#define zero(s) memset(((char*)&s),sizeof(s),0);
  zero(location);

  //city longitude and latitude calculations are to be done here
  switch (c) {
  case Cairo:
    location.degreeLong=31.40;
    location.degreeLat=30.13;
    //location.pressure=1010; // are these defaulted ??
    //location.temperature=10;
    break;
  case Rennes: // http://pgj.pagesperso-orange.fr/latlong.htm
    location.degreeLong=-1.667;
    location.degreeLat=48.100;
    location.seaLevel=30; // http://dateandtime.info/citycoordinates.php?id=2983990
    // of 4-Jan-2014:
    location.temperature=10;
    location.pressure=998;
    break;
  }  
}


Method method;
//better than the number layout used in prayer.h
enum METHOD {None, Egypt, UISK_Shafi, UISK_Hanafi, 
	     North_America, Muslim_League, Umm_AlQurra,
	     Fixed_Ishaa};

void set_method(enum METHOD m){
#define zero(s) memset(((char*)&s),sizeof(s),0);
  zero(method);
  getMethod((int)m,&method);
  // DEBUGGING
  
  printf("Fajr angle = %f\n",method.fajrAng);
  printf("Ishaa angle = %f\n",method.ishaaAng);
  printf("Angle difference between Imsaak and Fajr = %f\n",method.imsaakAng);
  printf("Minutes between Fajr and Shurooq = %d\n",method.fajrInv);
  printf("Minutes between Ishaa and Maghrib = %d\n",method.ishaaInv);
  printf("Minutes between Imsaak and Fajr = %d\n",method.imsaakInv);
  char *rounding_methods_str[]={"No rounding","Normal","Normal, except round down for Shurooq and Imsaak","Aggressive"};
  printf("Rounding seconds %s\n",rounding_methods_str[method.round]);
  printf("Assr mazhab = %s\n",method.mathhab==1?"Shaf'i":"Hanafi");
  printf("Latitude used for 'nearest latitude' extreme = %f\n",method.nearestLat);
  char *extreme_str[]={
      "none. if unable to calculate, leave as 99:99",
       "Nearest Latitude: All prayers Always",
       "Nearest Latitude: Fajr Ishaa Always",
       "Nearest Latitude: Fajr Ishaa if invalid",
       "Nearest Good Day: All prayers Always",
       "Nearest Good Day: Fajr Ishaa if invalid (default)",
       "1/7th of Night: Fajr Ishaa Always",
       "1/7th of Night: Fajr Ishaa if invalid",
       "1/7th of Day: Fajr Ishaa Always",
       "1/7th of Day: Fajr Ishaa if invalid",
       "Half of the Night: Fajr Ishaa Always",
       "Half of the Night: Fajr Ishaa if invalid",
       "Minutes from Shorooq/Maghrib: Fajr Ishaa Always (e.g. Maghrib=Ishaa)",
       "Minutes from Shorooq/Maghrib: Fajr Ishaa If invalid"
  };
  printf("Extreme latitude method = %s\n",extreme_str[method.extreme]);
  printf("Offset (yes/no) = %s\n",method.offset?"yes":"no");
  printf("Offset list = {Fajr:%f,Shurooq:%f,Dhuhr:%f,Asr:%f,Maghrib:%f,Ishaa:%f}\n",method.offList[0],method.offList[1],method.offList[2],method.offList[3],method.offList[4],method.offList[5]);
}

//returns the index of the next prayer from the prayers array
//returns 7 if the next prayer is fajr of tomorrow
int get_next_prayer (Prayer prayers[]){
  int hour, min,sec, next_met=0,p;
  get_time (&hour, &min,&sec);
  int ret;
  for (p=0; p<6; p++)
  {
    if (prayers[p].hour > hour //removed "=" since its handled in the next condition.
	|| (prayers[p].hour == hour && prayers[p].minute > min)
	|| (prayers[p].hour == hour &&prayers[p].minute == min 
	    && prayers[p].second > sec)) //must check for seconds to avoid a bug in get_time_to_prayer
      {
	ret = p;//save the index
   	next_met=1;
	break;
      }
  }
  if (next_met) {
    return ret;
  }
  else //means time is after ishaa of today and before midnight
    {
    Date date;
    get_today(&date);
    getNextDayFajr(&location,&method,&date,&prayers[0]);
    return 7; //fajr of TOMORROW is the next prayer
  }
}

void get_time_to_prayer(Prayer p,struct tm *ts){
  int hour, min, sec;
  get_time(&hour, &min, &sec);
  ts -> tm_hour = p.hour - hour;
  ts-> tm_min = p.minute - min;
  ts-> tm_sec =  p.second - sec;
  
  if (ts-> tm_sec <0){ //fix seconds
    ts-> tm_sec +=60;
    ts-> tm_min -=1;
  }
  if(ts-> tm_min <0){ //fix minutes
    ts-> tm_min +=60;
    ts-> tm_hour -=1;
  }
  if(ts-> tm_hour <0){ //fix hours
   ts-> tm_hour +=24;
  }
}

int main(int argc, const char *argv[])
{
  set_location(Rennes);
  /* Egypt, UISK_Shafi, UISK_Hanafi,  */
  /* 	     North_America, Muslim_League, Umm_AlQurra, */
  /* 	     Fixed_Ishaa */
  set_method(Fixed_Ishaa);
  Date date;
  Prayer prayers[6];
  //zero them out
#define zero(s) memset(((char*)&s),sizeof(s),0);
  zero(date);
  zero(prayers);
  get_today(&date);
  getPrayerTimes(&location,&method,&date,prayers);
  int next_prayer=get_next_prayer(prayers);

  if(next_prayer==7){    //if the next prayer is fajr of tomorrow
    next_prayer=0;    //set next prayer to fajr for the printing loop
  }
    

  int c = printf("Prayer times for day : %02d/%02d/%02d %+.1f GMT %s\n",
         date.day,
         date.month,
         date.year,
         location.gmtDiff,
         location.dst?"(+ extra 1 for day-light saving time)":""
         );
  while(--c) printf("-");
  printf("\n");

  const char *pnames[] = {
    "Fajr   ",
    "Shorooq",
    "Dhuhr  ",
    "Asr    ",
    "Maghrib",
    "Ishaa  "
  };
  textcolor(BRIGHT, WHITE, BLACK);
  int p;
  for(p=0; p<6; p++)
  {
    printf("%s    |",pnames[p]);
  }
  printf("\n");
  
  for (p=0; p<6; p++){
    if (p == next_prayer)
      textcolor(BRIGHT, WHITE, GREEN); //highlight the next prayer

    printf("%02d:%02d:%02d %s|",
           prayers[p].hour<=12?prayers[p].hour:prayers[p].hour-12,
           prayers[p].minute,
           prayers[p].second,
           prayers[p].hour>=12?"PM":"AM"
           );
    if (p == next_prayer)
      textcolor(RESET, WHITE, BLACK); //restore the font
  }
  // TODO: reset the font of the terminal before exit.
  printf("\n");
  
  struct tm ts;
  get_time_to_prayer(prayers[next_prayer],&ts);
  printf("Time to %s%02d:%02d:%02d",
	 pnames[next_prayer],
	 ts.tm_hour<= 12? ts.tm_hour: ts.tm_hour-12,
	 ts.tm_min,
	 ts.tm_sec
	 );
  putchar('\n');
  return 0;
}

/*
  Command-line arguments:
  1- Date: -d/--date
   1.1: Specific date ( 13/9/2009 ) <dd/mm/yyyy>
   1.2: Specify date range --range
    1.2.1: Range in days --days <days> # days from today and today+days
    1.2.2: Range in  months --months <months> # months from this month to month+months
    1.2.3: Range in years --years <years> # i think you got it already
    1.2.4: Free form start/end <dd/mm/yyyy> <dd/mm/yyyy>
   1.3: Predefined (Week, Month, 3-Months, Year) --week/--month/--quarter/--year
   1.4: Hijri date --hijri # interpret all dates as hijri dates
   1.5: Different input formats # NOT YET ...

  2- Location:
   2.1: Longtitude/Latitude/Gmt/[Dst]. --long <long> --lat <lat> / -l<long>,<lat>
   2.2: Predefine list of cities (or at least read from ~/.something) -lcairo --location cairo
   2.3: Sea level/pressure/temperature # NOT YET ...

  3- Method:
   3.1 Predefined methods: -m1 --method 1
       0: none. Set to default or 0 # NOT USED IN CMD-LINE !!
       1: Egyptian General Authority of Survey
       2: University of Islamic Sciences, Karachi (Shaf'i)
       3: University of Islamic Sciences, Karachi (Hanafi)
       4: Islamic Society of North America
       5: Muslim World League (MWL)
       6: Umm Al-Qurra, Saudi Arabia
       7: Fixed Ishaa Interval (always 90)
       8: Egyptian General Authority of Survey (Egypt)
   3.2: Personal tweaking of angels # NOT YET ...
   3.3: Extreme methods: struct-Method.extreme # NOT YET ...

  2/3 load location + city from conf. # -f/--file <conf file name> # or read from ~/.prayer by if existing

  4- Output:
   4.1: Print Imsaak time -i/--imsaak
   4.2: Print Qibla direction -q/--qibla
   4.3: Calendar -c/--calendar
    4.3.1: Ramadan-style calendar ( Calendar + Imsaak ) -r/--ramadan
   4.4: Silent (Prints only hours, not prayer names) -s --silent
   4.5: Linear for a range of dates (not Calendar format) -t/--tabular
   4.6: Output format : %h:%m:%S or %s, 24 or 12 hours format, ... etc. (See 4.1: Extreme-flag printing) # NOT YET ...
  5- Misc:
   5.1: --version
   5.2: --help # NOT YET ...
 */

